package com.czkevin.biliwsclient;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.Getter;

import java.io.IOException;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Deque;
import java.util.Random;
import java.util.concurrent.ConcurrentLinkedDeque;

public class DanmuSender extends Thread {

    private boolean running = false;

    @Getter
    public final String userAgent;

    public final BiliSession session;

    public final long interval;

    public String lastRoom = null;
    public String lastSend = null;

    private boolean dontExitUntilEmpty = true;

    @Getter
    public long lastSendTime = 0L;

    public final Deque<QueuedDanmu> queue = new ConcurrentLinkedDeque<>();

    private final Random random = new Random();

    private final String fmt;

    public DanmuSender(BiliSession session, String requestFormat) {
        this(BiliLiveClient.DEFAULT_USER_AGENT, session, 1500L, requestFormat);
    }

    public DanmuSender(String userAgent, BiliSession session, long interval, final String fmt) {
        this.userAgent = userAgent;
        this.session = session;
        this.interval = interval;
        this.fmt = fmt;

        start();
    }

    @Override
    public synchronized void start() {
        if(running) throw new IllegalStateException("Already running! ");
        running = true;
        super.start();
    }

    public void stopSender() {
        stopSender(true);
    }

    public void stopSender(boolean dontExitUntilEmpty) {
        running = false;
        this.dontExitUntilEmpty = dontExitUntilEmpty;
    }

    private static final int LEN_LIMIT = 19;
    public void send(String room, String text) {
        if(text.length() <= LEN_LIMIT) {
            queue.addLast(new QueuedDanmu(room, "^" + text));
        } else {
            int cutCount = text.length() / LEN_LIMIT;
            if(text.length() % LEN_LIMIT > 0) cutCount ++;
            String[] cuts = new String[cutCount];
            for(int i = 0; i < cutCount; i++) {
                int endIndex = (i+1)*LEN_LIMIT;
                if(endIndex > text.length()) endIndex = text.length();
                cuts[i] = text.substring(i * LEN_LIMIT, endIndex);
            }
            for(String c : cuts) queue.addLast(new QueuedDanmu(room, "^" + c));
        }
    }

    @Override
    public void run() {
        while (running || (dontExitUntilEmpty && queue.size() > 0)) {
            QueuedDanmu queued = queue.poll();
            if (queued != null) {
                if (System.currentTimeMillis() - queued.createTime > 10000L) continue;

                try {
                    sendNow(queued.room, queued.text);
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
            try {
                Thread.sleep(interval + random.nextInt(500));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void sendNow(String room, String text) throws IOException {
        if(lastRoom!= null && lastSend != null && lastRoom.equals(room) && lastSend.equals(text)) return;

        HttpURLConnection connection = (HttpURLConnection) new URL("https://api.live.bilibili.com/msg/send").openConnection();
        connection.setRequestMethod("POST");
        connection.setRequestProperty("authority", "api.live.bilibili.com");
        connection.setRequestProperty("dht", "1");
        connection.setRequestProperty("content-type", "application/x-www-form-urlencoded; charset=UTF-8");
        connection.setRequestProperty("user-agent", userAgent);
        connection.setRequestProperty("origin", "https://live.bilibili.com");
        connection.setRequestProperty("referer", String.format("https://live.bilibili.com/%s", room));
        connection.setRequestProperty("accept-language", "en,zh-CN;q=0.9,zh;q=0.8");
        connection.setRequestProperty("cookie", String.format("_uuid=%s; SESSDATA=%s; bili_jct=%s", session.uuid, session.session, session.biliJct));
        connection.setDoOutput(true);
        connection.setDoInput(true);
        try(OutputStream os = connection.getOutputStream()) {
            final String encoded_text = URLEncoder.encode(text, "UTF-8");
            os.write(
                    fmt
                            .replace("__TEXT__", encoded_text)
                            .replace("__ROOM__", room)
                            .replace("__TOKEN__", session.biliJct)
                            .getBytes(StandardCharsets.UTF_8)
            );
        }
        byte[] ret = StreamHelper.readFully(connection.getInputStream());
        String sRet = new String(ret, StandardCharsets.UTF_8);
        System.out.println(sRet);
        JSONObject jRet = JSON.parseObject(sRet);
        int code = jRet.getInteger("code");
        String message = jRet.containsKey("message") ? jRet.getString("message") : "<NULL>";
        if(code != 0) throw new IOException(String.format("Failed sending message(%d): %s", code, message));

        lastSendTime = System.currentTimeMillis();
        lastSend = text;
        lastRoom = room;
    }


    public static class QueuedDanmu {
        public final String room;
        public final String text;

        public final long createTime = System.currentTimeMillis();

        public QueuedDanmu(String room, String text) {
            this.room = room;
            this.text = text;
        }
    }
}
